package tau.pimpmyfreedb.master;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.ini4j.IniPreferences;
import org.ini4j.InvalidFileFormatException;

import tau.pimpmyfreedb.GUI.AddExistWindow;
import tau.pimpmyfreedb.GUI.EditWindow;
import tau.pimpmyfreedb.GUI.EditWindow.EditWindowMode;
import tau.pimpmyfreedb.GUI.MainWindow;
import tau.pimpmyfreedb.GUI.Predicate;
import tau.pimpmyfreedb.GUI.SWTUtil;
import tau.pimpmyfreedb.GUI.WaitWindow;
import tau.pimpmyfreedb.GUI.WelcomeWindow;
import tau.pimpmyfreedb.common.IProgressUpdateClient;
import tau.pimpmyfreedb.common.types.Album;
import tau.pimpmyfreedb.common.types.Artist;
import tau.pimpmyfreedb.common.types.EditData;
import tau.pimpmyfreedb.common.types.MusicItem;
import tau.pimpmyfreedb.common.types.PagedSearchResult;
import tau.pimpmyfreedb.common.types.Track;
import tau.pimpmyfreedb.common.types.TrackLength;
import tau.pimpmyfreedb.common.types.User;
import tau.pimpmyfreedb.dal.DalResponse;
import tau.pimpmyfreedb.dal.DataFilter;
import tau.pimpmyfreedb.dal.DataType;
import tau.pimpmyfreedb.dal.IDataItem;
import tau.youtube.YouTubeAPI;
import tau.youtube.YouTubeAPI.PlayerType;

public class PimpMaster implements IPimpMaster {

    // private data members

    private IniPreferences config;
    private UnifiedDalBridge dal;
    private boolean exitOnWelcomeClose;
    private boolean closeFlag;
    private EditData editData;

    // temps

    private MusicItem[] tempTrackList;

    // windows

    private MainWindow mainWindow = null;
    private WelcomeWindow welcomeWindow = null;
    private AddExistWindow addExistWindow = null;
    protected MusicItem[] tempAlbumsList;

    public PimpMaster(String configFilePath) throws InvalidFileFormatException,
	    IOException {
	// read the config file
	readPref(configFilePath);

	// init the windows
	mainWindow = new MainWindow(this);
	welcomeWindow = new WelcomeWindow(this);

	// init status vars
	exitOnWelcomeClose = true;
	closeFlag = false;
    }

    // --- settings --- //

    private void readPref(String configFilePath)
	    throws InvalidFileFormatException, IOException {
	FileInputStream file = new FileInputStream(configFilePath);
	config = new IniPreferences(file);
	file.close();
    }

    // --- general ---//

    public void startApplication() {

	// connect to db
	welcomeWindow.setStatus("Connecting to the Database...");
	welcomeWindow.disableControls();
	try {
	    connectToDB(new GuiCallback<DalResponse>(welcomeWindow.getShell()) {

		@Override
		public void guiExecute(DalResponse param) {

		    if (param.isSucceeded()) {
			welcomeWindow.updateUsersList();
		    } else {
			welcomeWindow.showDBErrorMessage(param.getMessage());
			welcomeWindow.setStatus("DB connection error. Edit config/PimpConfig.ini to validate settings are correct.");
		    }
		}
	    });
	} catch (ClassNotFoundException e) {
	    welcomeWindow.setStatus(e.getMessage());
	}

	// open the welcome window
	welcomeWindow.getShell().open();
	welcomeWindow.getShell().setFocus();
	welcomeWindow.getShell().setActive();

	// start the readAndDispatch loop
	SWTUtil.readAndDispatch(new Predicate() {

	    @Override
	    public boolean isTrue() {
		return !closeFlag;
	    }
	});

	try {
	    // close
	    WaitWindow wait = new WaitWindow(false);
	    wait.open("Closing...");

	    if (dal.isConnected())
		dal.Disconnect();
	    if (welcomeWindow != null)
		welcomeWindow.getShell().dispose();
	    if (mainWindow != null)
		mainWindow.getShell().dispose();
	    wait.close();
	    SWTUtil.dispose();
	} catch (Exception ex) {
	    ex.printStackTrace();
	}

    }

    public void close() {
	this.closeFlag = true;
    }

    // --- dal --- //

    public void connectToDB(Callback<DalResponse> callback)
	    throws ClassNotFoundException {

	// read configurations
	String url = config.node("sql").get("url", null);
	String username = config.node("sql").get("username", null);
	String password = config.node("sql").get("password", null);
	// int minSize = config.node("thread_pool").getInt("minSize", 3);
	// int maxSize = config.node("thread_pool").getInt("maxSize", 10);
	int queueSize = config.node("thread_pool").getInt("queueSize", 100);
	int idleTime = config.node("thread_pool").getInt("idleTime", 90);
	long closeTimeout = config.node("thread_pool").getLong("closeTimeout",
		5);

	// init the dal layer
	dal = new UnifiedDalBridge(1, 1, idleTime, queueSize, closeTimeout);

	// connect
	dal.Connect(url, username, password, callback);
    }

    public UnifiedDalBridge getDal() {
	return dal;
    }

    // --- import ---//

    @Override
    public void importData() {
	// open dialog to get the file path
	String[] paths = mainWindow.openImportDialog();

	if (paths.length == 0) {
	    return;
	}

	// try to import
	try {
	    WaitWindow waitWindow = mainWindow.openWaitWindow(
		    "Importing Please Wait...", false);
	    dal.importData(paths, waitWindow, new GuiCallback<DalResponse>(
		    mainWindow.getShell()) {
		@Override
		public void guiExecute(DalResponse param) {
		    mainWindow.closeWaitWindow();
		    SWTUtil.messageBox(param.getMessage());
		}
	    });
	} catch (FileNotFoundException e) {
	    SWTUtil.errorBox(e);
	} catch (IOException e) {
	    SWTUtil.errorBox(e);
	}
    }

    // --- welcome window ---//

    public void login(int userId) {
	exitOnWelcomeClose = false;
	welcomeWindow.getShell().close();

	// get the user friends
	dal.getFriendsByUser(userId,
		new GuiCallback<IDataItem[]>(mainWindow.getShell()) {

		    @Override
		    public void guiExecute(IDataItem[] param) {
			mainWindow.setFriends(param);
		    }

		});

	// open the main window
	mainWindow.getShell().open();
	mainWindow.getShell().setFocus();
	mainWindow.getShell().setActive();
    }

    @Override
    public void addNewUser(User user, IProgressUpdateClient progressClient,
	    final Callback<DalResponse> callback) {
	dal.submitUser(user, progressClient, new GuiCallback<DalResponse>(
		welcomeWindow.getShell()) {
	    @Override
	    public void guiExecute(DalResponse param) {
		callback.execute(param);
	    }
	});
    }

    public void deleteUser(int userId) {
	dal.deleteItem(DataType.PIMP_USER, userId,
		new GuiCallback<DalResponse>() {
		    @Override
		    public void guiExecute(DalResponse param) {
			welcomeWindow.updateUsersList();
		    }
		});
    }

    public void closeWelcomeScreen() {
	if (exitOnWelcomeClose)
	    close();
    }

    // --- search --- //

    public void search() {

	// delay execution to prevent unnecessary requests to the db
	Timer timer = new Timer();
	timer.schedule(new TimerTask() {
	    @Override
	    public void run() {
		SWTUtil.getDisplay().asyncExec(new Runnable() {
		    @Override
		    public void run() {
			// Build the filter
			final DataFilter filter = new DataFilter();
			filter.setUsePaging(true);
			filter.setPageIndex(mainWindow.getCurrentPage());
			String searchString = mainWindow.getSearchString()
				.toLowerCase();
			filter.setSearchString(searchString);
			DataType type = mainWindow.getCategory();
			filter.setReturnType(type);
			// filter.setPageIndex(mainWindow.getPageIndex());
			// filter.setPageSize(mainWindow.PAGE_SIZE);

			// Search
			search(new SearchInfo(searchString, type, filter));
		    }
		});
	    }
	}, 300);
    }

    private void search(final SearchInfo searchInfo) {
	final DataFilter filter = searchInfo.getFilter();

	dal.searchMusic(searchInfo.getFilter(),
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {
		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			mainWindow.addHistoryItem(searchInfo.getSearchString(),
				filter.getReturnType(), filter);
			mainWindow.setSearchResult(param);
			mainWindow.setResultTitle(searchInfo);
			mainWindow.updateCategoryDetails();
		    }
		});
    }

    private void refreshLastSearch() {
	SearchInfo last = mainWindow.getLastHistoryItem();
	if (last != null) {
	    mainWindow.removeLastHistoryItem();
	    search(last);
	}
    }

    public void back() {

	// remove current search from history
	mainWindow.removeLastHistoryItem();

	// get previous search
	SearchInfo lastSearch = mainWindow.getLastHistoryItem();
	if (lastSearch == null)
	    return;
	mainWindow.setSearchString(lastSearch.getSearchString());
	mainWindow.setCategory(lastSearch.getSearchType());

	// remove that search too because we add it right away in the search()
	mainWindow.removeLastHistoryItem();

	search(lastSearch);
    }

    // --- music item --- //

    @Override
    public void playOrShow() {
	// delay execution to prevent race condition between
	// mainWindow.resultsList.mouseListener and
	// mainWindow.resultsList.selectionListener
	Timer timer = new Timer();
	timer.schedule(new TimerTask() {
	    @Override
	    public void run() {
		SWTUtil.getDisplay().asyncExec(new Runnable() {
		    @Override
		    public void run() {
			MusicItem item = mainWindow.getSelectedItem();
			if (item.getDataType() == DataType.TRACK)
			    play(item);
			else
			    show(item);
		    }
		});
	    }
	}, 200);
    }

    private void play(final MusicItem track) {

	// decide the player mode
	final String strPlayerMode = config.node("youtube").get("player_mode",
		"pc");

	// find the artist of this track

	DataFilter filter = new DataFilter();
	filter.setRestrictByItem(track);
	filter.setReturnType(DataType.ARTIST);

	dal.searchMusic(
		filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>(mainWindow
			.getShell()) {

		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			if (param.getResult().length == 0) {
			    SWTUtil.errorBox("Couldn't find artist for track.");
			    return;
			}
			// decide the player mode
			YouTubeAPI.PlayerType playerMode = PlayerType.PC_PLAYER;
			if (strPlayerMode == "pc")
			    playerMode = PlayerType.PC_PLAYER;
			else if (strPlayerMode == "mac")
			    playerMode = PlayerType.MAC_PLAYER;

			// find the movie on YouTube
			dal.GetYouTubeURL(
				track.getName(),
				param.getResult()[0].getName(),
				playerMode,
				new GuiCallback<DalResponse>(mainWindow
					.getShell()) {

				    @Override
				    public void guiExecute(DalResponse param) {

					// play the video

					if (param.isSucceeded()) {
					    mainWindow.setBrowserURL(param
						    .getMessage());

					    if (param.isSucceeded()) {
						mainWindow.setBrowserURL(param
							.getMessage());

						mainWindow.setVideoTitle(track
							.getName());
					    } else {
						SWTUtil.errorBox(param
							.getMessage());
					    }
					} else {
					    SWTUtil.errorBox(param.getMessage());
					}
				    }
				});

		    }
		});
    }

    private void show(final MusicItem item) {

	// set the filter
	final DataFilter filter = new DataFilter();
	filter.setRestrictByItem(item);

	final DataType type = item.getDataType();
	switch (type) {
	case ALBUM:
	    filter.setReturnType(DataType.TRACK);
	    mainWindow.setCategory(DataType.TRACK);
	    break;
	case ARTIST:
	    filter.setReturnType(DataType.ALBUM);
	    mainWindow.setCategory(DataType.ALBUM);
	    break;
	default:
	    // if it's not an artist or an album - ignore
	    return;
	}

	// set the search string
	mainWindow.setSearchString("");

	// search
	search(new SearchInfo(item.getName(), filter.getReturnType(), filter));
    }

    public void showItemDetails() {
	final MusicItem item = mainWindow.getSelectedItem();

	DataType type = item.getDataType();
	switch (type) {
	case ALBUM:
	    showAlbumDetails(item);
	    break;
	case ARTIST:
	    showArtistDetails(item);
	    break;
	case TRACK:
	    showTrackDetail((Track) item);
	    break;
	default:
	    break;
	}
    }

    private void showAlbumDetails(final MusicItem album) {
	final DataFilter filter = new DataFilter();
	filter.setRestrictByItem(album);

	final StringBuilder msg = new StringBuilder();
	msg.append("Album: ").append(album.getName()).append("\n\n");
	msg.append("Details:\n").append(album.getExtendedData());

	// get artists
	filter.setReturnType(DataType.ARTIST);
	dal.searchMusic(filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {
		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			msg.append("\n\nArtist(s): ");
			for (int i = 0; i < param.getResult().length; i++) {
			    msg.append(param.getResult()[i].getName());
			    if (i != param.getResult().length - 1)
				msg.append(", ");
			}

			// get tracks
			filter.setReturnType(DataType.TRACK);
			dal.searchMusic(
				filter,
				new GuiCallback<PagedSearchResult<MusicItem[]>>() {
				    @Override
				    public void guiExecute(
					    PagedSearchResult<MusicItem[]> param) {
					msg.append("\n\nTracks:\n");
					for (int i = 0; i < param.getResult().length; i++) {
					    msg.append("  ")
						    .append(i + 1)
						    .append(". ")
						    .append(param.getResult()[i]
							    .getName())
						    .append("\n");
					}

					// update details window
					mainWindow.setDetails(msg.toString());
				    }
				});
		    }
		});
    }

    private void showArtistDetails(final MusicItem artist) {
	final DataFilter filter = new DataFilter();
	filter.setRestrictByItem(artist);
	filter.setReturnType(DataType.TRACK);

	final StringBuilder msg = new StringBuilder();
	msg.append("Artist: ").append(artist.getName()).append("\n\n");
	msg.append("Details:\n").append(artist.getExtendedData());

	// get albums & tracks
	reqGetArtistAlbums(artist, 0, msg);
    }

    private void reqGetArtistAlbums(final MusicItem artist,
	    final int albumIndex, final StringBuilder res) {

	// set the filer
	DataFilter filter = new DataFilter();
	filter.setRestrictByItem(artist);
	filter.setReturnType(DataType.ALBUM);
	filter.setPageSize(1);
	filter.setPageIndex(albumIndex);

	// search
	dal.searchMusic(filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {

		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			if (param.getResult().length > 0) {

			    // add the current album name
			    res.append("\n\nTracks for album \"")
				    .append(param.getResult()[0].getName())
				    .append("\":\n");

			    // add the current album tracks
			    reqGetTracksForAlbumOfArtist(param.getResult()[0],
				    artist, albumIndex, res);
			} else {

			    // finished the recursion - update details window
			    mainWindow.setDetails(res.toString());
			}
		    }
		});
    }

    private void reqGetTracksForAlbumOfArtist(MusicItem album,
	    final MusicItem artist, final int albumIndex,
	    final StringBuilder res) {

	// set the filter
	DataFilter filter = new DataFilter();
	filter.setRestrictByItem(album);
	filter.setReturnType(DataType.TRACK);

	// search
	dal.searchMusic(filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {

		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {

			// add the current album's tracks
			for (int i = 0; i < param.getResult().length; i++) {
			    res.append("  ").append(i + 1).append(". ")
				    .append(param.getResult()[i].getName());
			    if (i != param.getResult().length - 1)
				res.append("\n");
			}

			// add the next album's tracks
			reqGetArtistAlbums(artist, albumIndex + 1, res);
		    }
		});
    }

    private void showTrackDetail(final Track track) {

	final StringBuilder msg = new StringBuilder();
	msg.append("Track: ").append(track.getName()).append("\n\n");
	msg.append("Track Length: ").append(track.getLength());

	// get album
	final DataFilter albumFilter = new DataFilter();
	albumFilter.setSpecificIDToGet(track.getAlbumID());
	albumFilter.setReturnType(DataType.ALBUM);
	dal.searchMusic(albumFilter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {

		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			msg.append("\nAlbum: ");
			if (param.getResult().length > 0)
			    msg.append(param.getResult()[0].getName());
			else
			    msg.append("Unknown");

			// get artist
			final DataFilter artistFilter = new DataFilter();
			artistFilter.setReturnType(DataType.ARTIST);
			artistFilter.setRestrictByItem(track);
			dal.searchMusic(
				artistFilter,
				new GuiCallback<PagedSearchResult<MusicItem[]>>() {

				    @Override
				    public void guiExecute(
					    PagedSearchResult<MusicItem[]> param) {
					msg.append("\nArtist(s): ");
					for (int i = 0; i < param.getResult().length; i++) {
					    msg.append(param.getResult()[i]
						    .getName());
					    if (i != param.getResult().length - 1)
						msg.append(", ");
					}
					msg.append("\nDetails:\n").append(
						track.getExtendedData());

					// update details window
					mainWindow.setDetails(msg.toString());
				    }
				});

		    }
		});
    }

    // --- likes --- //

    @Override
    public void showLikes() {

	// get the friend
	IDataItem friend = mainWindow.getSelectedFriend();

	// search
	dal.getLikesByFriend(friend.getDataId(), new GuiCallback<IDataItem[]>(
		mainWindow.getShell()) {

	    @Override
	    public void guiExecute(IDataItem[] param) {
		mainWindow.setLikes(param);
	    }
	});
    }

    public void searchCurrentLike() {
	IDataItem like = mainWindow.getSelectedLike();

	String searchString = like.getName();
	Pattern cleanSearchString = Pattern
		.compile("(\\(.*?\\))|(\\[.*?\\])|(\".*?\")");
	Matcher cleaner = cleanSearchString.matcher(searchString);
	searchString = cleaner.replaceAll("");

	// search
	mainWindow.setSearchString(searchString);
	mainWindow.setCategory(DataType.ARTIST);
	search();
    }

    public void addDataType(EditWindow editWindow, DataType dataType) {

	addExistWindow = new AddExistWindow(this, editWindow);
	addExistWindow.setMode(dataType);
	addExistWindow.run();
	if (addExistWindow.getAnswer()) {
	    // editData.addRelation(new Tuple<MusicItem,
	    // MusicItem>(editWindow.getCurrentMusicItem(),
	    // addExistWindow.getSelection()));
	    editWindow.addMusicItem(addExistWindow.getSelection());
	}
    }

    public void searchSmall() {

	// build the filter
	DataFilter filter = new DataFilter();
	String searchString = addExistWindow.getSearch().toLowerCase();
	filter.setSearchString(searchString);
	filter.setReturnType(addExistWindow.getDataType());

	// search
	dal.searchMusic(
		filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>(mainWindow
			.getShell()) {
		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			addExistWindow.setSearchResult(param.getResult());
		    }
		});
    }

    // --- delete --- //

    public void deleteMusicItem() {
	MusicItem item = mainWindow.getSelectedItem();

	switch (item.getDataType()) {
	case ALBUM:
	    deleteAlbum(item);
	    break;
	case ARTIST:
	    deleteArtist(item);
	    break;
	case TRACK:
	    deleteTrack(item);
	    break;
	default:
	    return;
	}
    }

    @Override
    public void createNewItem(AddExistWindow addExistWindow, DataType dataType) {
	EditWindow editWindow = new EditWindow(addExistWindow.getShell(), this,
		addExistWindow, false);
	if (dataType == DataType.ALBUM) {
	    editWindow.setAlbumMode(null, "", "New Album", null, "", "");
	    editWindow.run();
	} else if (dataType == DataType.TRACK) {
	    editWindow.setTrackMode(null, "New Track", "", "", "", "");
	    editWindow.run();
	}

    }

    @Override
    public void handleEditResults(EditWindow editWindow) {
	// create the new music item if needed
	if (editWindow.getMode() == EditWindowMode.ADD) {
	    MusicItem temp;
	    switch (editWindow.getDataType()) {
	    case ALBUM:
		temp = new Album(IDataItem.NewItemId, editWindow.newName,
			editWindow.newGenere,
			SWTUtil.String2Int(editWindow.newYear),
			editWindow.newDesc);
		break;
	    case TRACK:

		temp = new Track(IDataItem.NewItemId, editWindow.newName,
			Track.UKNOWN_ALBUM_ID, new TrackLength(
				SWTUtil.String2Int(editWindow.newLengthMin),
				SWTUtil.String2Int(editWindow.newLengthSec)),
			editWindow.newDesc);
		break;
	    case ARTIST:
		temp = new Artist(IDataItem.NewItemId, editWindow.newName,
			editWindow.newDesc);
		break;
	    default:
		temp = null;
		break;
	    }

	    editData.addItemToCreate(temp);

	    // add relations
	    editData.addRelations(temp, editWindow.getRelationsList());
	    editData.addOldRelations(temp, editWindow.getOldRelationsList());
	    if (editWindow.getAddExistWindow() != null) {
		editWindow.getAddExistWindow().getEditWindow()
			.addMusicItem(temp);
	    }

	    // close calling window
	    if (editWindow.getAddExistWindow() != null) {
		editWindow.getAddExistWindow().getShell().close();
	    }

	    if (editWindow.getDataType() == DataType.ARTIST) {
		// finish
		SWTUtil.printEditData(editData);
		updateDalWithEditData();
	    }

	} else if (editWindow.getMode() == EditWindowMode.EDIT) {
	    if (editWindow.getRelationsList() != null) {
		editData.addRelations(editWindow.getCurrentMusicItem(),
			editWindow.getRelationsList());
	    }
	    if (editWindow.getOldRelationsList() != null) {
		editData.addOldRelations(editWindow.getCurrentMusicItem(),
			editWindow.getOldRelationsList());
	    }
	    editData.setMusicItemToUpdate(editWindow.getCurrentMusicItem());

	    // finish
	    SWTUtil.printEditData(editData);
	    updateDalWithEditData();
	}
	editWindow.close();
    }

    private void updateDalWithEditData() {
	// Create
	dal.updateDataFromGui(editData, new GuiCallback<DalResponse>() {

	    @Override
	    public void guiExecute(DalResponse param) {
		if (!param.isSucceeded()) {
		    SWTUtil.errorBox(param.getMessage());
		}
		refreshLastSearch();
		editData = null;
	    }

	});
    }

    public void deleteAlbum(final MusicItem album) {
	// find related tracks
	DataFilter filter = new DataFilter();
	filter.setRestrictByItem(album);
	filter.setReturnType(DataType.TRACK);

	dal.searchMusic(filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {
		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			// prompt
			String msg = "Are you sure you want to delete \""
				+ album.getName() + "\" ?";
			if (param.getResult().length > 0)
			    msg += "\n\nThe following tracks will also be deleted:\n\n";
			for (MusicItem track : param.getResult())
			    msg += track.getName() + "\n";
			boolean del = mainWindow.confirmBox(msg);

			// delete
			if (del)
			    deleteSingleMusicItem(album);
		    }
		});
    }

    public void deleteArtist(final MusicItem artist) {
	// find related tracks
	DataFilter filter = new DataFilter();
	filter.setRestrictByItem(artist);
	filter.setReturnType(DataType.ALBUM);

	dal.searchMusic(filter,
		new GuiCallback<PagedSearchResult<MusicItem[]>>() {
		    @Override
		    public void guiExecute(PagedSearchResult<MusicItem[]> param) {
			// prompt
			String msg = "Are you sure you want to delete \""
				+ artist.getName() + "\" ?";
			if (param.getResult().length > 0)
			    msg += "\n\nThis artist has the following albums linked with it :\n\n";
			for (MusicItem album : param.getResult())
			    msg += album.getName() + "\n";
			if (param.getResult().length > 0)
			    msg += "\nPlease not that these album will NOT be deleted unless you delete them manually\n";
			boolean del = mainWindow.confirmBox(msg);

			// delete
			if (del)
			    deleteSingleMusicItem(artist);
		    }
		});

    }

    public void deleteTrack(MusicItem track) {
	String msg = "Are you sure you want to delete \"" + track.getName()
		+ "\" ?";
	if (mainWindow.confirmBox(msg))
	    deleteSingleMusicItem(track);
    }

    private void deleteSingleMusicItem(final MusicItem item) {
	dal.deleteItem(item.getDataType(), item.getDataId(),
		new Callback<DalResponse>() {
		    @Override
		    public void execute(DalResponse param) {
			SWTUtil.messageBox("\"" + item.getName() + "\" deleted");
			search(mainWindow.getLastHistoryItem());
		    }
		});
    }

    public void removeItem(EditWindow editWindow) {
	MusicItem temp = editWindow.getSelectedMusicItem();
	editData.removeData(temp);
	editWindow.removeMusicItem(temp);

    }

    public void handleSelectedDataType(AddExistWindow addExistWindow) {
	addExistWindow.getEditWindow().addMusicItem(
		addExistWindow.getSelection());
	addExistWindow.getShell().close();

    }

    @Override
    public void handleCancelEdit(EditWindow editWindow) {
	if (editWindow.isTopEditWindow()) {
	    // mode == EDIT Canceling the edit, get rid of the EditData
	    // stored.
	    editData = null;
	}
	// Just canceling the adding, the edit process may continue
	editWindow.close();
    }

    // --- edit --- //
    
    @Override
    public void edit() {
        final MusicItem item = mainWindow.getSelectedItem();
        final EditWindow editWindow = new EditWindow(mainWindow.getShell(),
        	this, EditWindowMode.EDIT, true);
        editData = new EditData();
        if (item.getDataType() == DataType.ALBUM) {
            DataFilter filter = new DataFilter();
            filter.setRestrictByItem(item);
            filter.setReturnType(DataType.TRACK);
            dal.searchMusic(
        	    filter,
        	    new GuiCallback<PagedSearchResult<MusicItem[]>>(mainWindow
        		    .getShell()) {
    
        		@Override
        		public void guiExecute(
        			PagedSearchResult<MusicItem[]> param) {
        		    tempTrackList = param.getResult();
        		    Album albumItem = (Album) item;
        		    editWindow.setAlbumMode(
        			    mainWindow.getSelectedItem(),
        			    (albumItem.getYear() + ""),
        			    albumItem.getName(), tempTrackList,
        			    albumItem.getGenre(),
        			    albumItem.getExtendedData());
        		    editWindow.run();
        		    // use the answer
    
        		}
        	    });
    
        } else if (item.getDataType() == DataType.ARTIST) {
            // Artist artistItem = (Artist) item;
            DataFilter filter = new DataFilter();
            filter.setRestrictByItem(item);
            filter.setReturnType(DataType.ALBUM);
            dal.searchMusic(
        	    filter,
        	    new GuiCallback<PagedSearchResult<MusicItem[]>>(mainWindow
        		    .getShell()) {
    
        		@Override
        		public void guiExecute(
        			PagedSearchResult<MusicItem[]> param) {
        		    tempAlbumsList = param.getResult();
        		    Artist artistItem = (Artist) item;
        		    editWindow.setArtistMode(
        			    mainWindow.getSelectedItem(),
        			    artistItem.getName(),
        			    artistItem.getExtendedData(),
        			    tempAlbumsList);
        		    editWindow.run();
        		    // use the answer
        		}
        	    });
        } else { // track
            final Track trackItem = (Track) item;
            DataFilter filter = new DataFilter();
            filter.setRestrictByItem(trackItem.getAlbumID(), DataType.ALBUM);
            filter.setReturnType(DataType.TRACK);
            dal.searchMusic(
        	    filter,
        	    new GuiCallback<PagedSearchResult<MusicItem[]>>(mainWindow
        		    .getShell()) {
    
        		@Override
        		public void guiExecute(
        			PagedSearchResult<MusicItem[]> param) {
        		    String album;
        		    if (param.getResult().length > 0) {
        			album = ((Track) param.getResult()[0])
        				.getName();
        		    } else {
        			album = "";
        		    }
    
        		    editWindow.setTrackMode(
        			    mainWindow.getSelectedItem(),
        			    trackItem.getName(), album, trackItem
        				    .getLength().getMinutes() + "",
        			    trackItem.getLength().getSeconds() + "",
        			    trackItem.getExtendedData());
        		    editWindow.run();
    
        		}
    
        	    });
    
        }
    }

    public void handleAddArtist() {
	final EditWindow editWindow = new EditWindow(mainWindow.getShell(),
		this, EditWindowMode.ADD, true);
	editData = new EditData();
	Callback<Integer> cb = new GuiCallback<Integer>() {

	    @Override
	    public void guiExecute(Integer param) {
		editWindow.setArtistMode(null, "New Artist", "", null);
		editWindow.run();
	    }
	};
	cb.execute(15);

    }
}
